package com.github.md11235.ideaproofofwork.startup

import com.intellij.openapi.diagnostic.thisLogger
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.ProjectActivity
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.event.DocumentEvent
import com.intellij.openapi.editor.event.DocumentListener
import com.intellij.openapi.editor.event.EditorFactoryEvent
import com.intellij.openapi.editor.event.EditorFactoryListener
import com.intellij.openapi.fileEditor.FileDocumentManager
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.diagnostic.Logger
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.io.ByteArrayOutputStream
import java.util.zip.GZIPOutputStream
import java.util.Base64
import java.io.File

import com.google.gson.Gson
import com.github.md11235.ideaproofofwork.cfiletracker.CFileTrackerUI
import com.github.md11235.ideaproofofwork.entity.FileChangeEvent
import com.github.md11235.ideaproofofwork.entity.ActionType
import com.github.md11235.ideaproofofwork.util.*
import com.google.gson.GsonBuilder
import com.intellij.openapi.Disposable

import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.roots.ContentIterator
import com.intellij.openapi.util.Disposer

// 1. escape strings to make them safe to be printed or stored
// todo: store changes in a text file along side the source file, for example main.c.steps
class MyProjectActivity : ProjectActivity {
    private val log = Logger.getInstance(MyProjectActivity::class.java)

    private var hasDocListenerBeenAdded = false

//    private val gson =  GsonBuilder().disableHtmlEscaping().create()
//
//    fun encodeFileToGzipBase64(virtualFile: VirtualFile): String {
//        virtualFile.inputStream.use { input ->
//            ByteArrayOutputStream().use { byteOutputStream ->
//                GZIPOutputStream(byteOutputStream).use { gzipStream ->
//                    input.copyTo(gzipStream)
//                }
//                return Base64.getEncoder().encodeToString(byteOutputStream.toByteArray())
//            }
//        }
//    }
//
//    fun getNewFilePath(virtualFile: VirtualFile, newFilename: String): String? {
//        return virtualFile.parent?.path?.let { parentPath ->
//            File(parentPath, newFilename).absolutePath
//        }
//    }
//
//    fun snapshotCSourceFiles(project: Project) {
//        val projectFileIndex = ProjectFileIndex.getInstance(project)
//
//        projectFileIndex.iterateContent(object : ContentIterator {
//            override fun processFile(fileOrDir: VirtualFile): Boolean {
//                if (!fileOrDir.isDirectory) {
//                    // Process the file here
//                    if (fileOrDir.extension == "c") {
//                        println("Taking a snapshot: ${fileOrDir.path}")
//                        val snapshotEvent = FileChangeEvent(System.currentTimeMillis(),
//                            ActionType.SNAPSHOT, 0, fileOrDir.path,
//                            encodeFileToGzipBase64(fileOrDir),
//                            "")
//
//                        val msg = gson.toJson(snapshotEvent)
//
//                        val logFile = getNewFilePath(fileOrDir, "${fileOrDir.name}.steps.txt")
//                        CFileTrackerUI.addEvent(msg)
//                        appendToLogFile(Path.of(logFile.toString()), msg)
//                        // given fileOrDir.path, read its content, gzip it and base64 encode into a string
//
//                    }
//                }
//                return true // Continue iteration
//            }
//        })
//    }

    override suspend fun execute(project: Project) {
        thisLogger().warn("Don't forget to remove all non-needed sample code files with their corresponding registration entries in `plugin.xml`.")

        snapshotCSourceFiles(project)

        // HACKED because: 我搞不定在关闭project时取消myDocListener
        if(hasDocListenerBeenAdded) {
            return
        }

        val myDocListener = object : DocumentListener {
            override fun documentChanged(event: DocumentEvent) {
                val document = event.document
                val file = FileDocumentManager.getInstance().getFile(document)

                if (file == null || file.extension != "c") return

                val srcFilepath = file.path.toNioPathOrNull()
                val srcFilename = srcFilepath?.fileName
                val logFile = srcFilepath?.parent?.resolve("${srcFilename}.steps.txt")
                log.info("Tracking ${file.path} using $logFile") // in repo ${repo.root.name}")

                val offset = event.offset
                val newText = event.newFragment.toString()
                val oldText = event.oldFragment.toString()

                val timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))

                val msg = when {
                    newText.isNotEmpty() && oldText.isEmpty() -> {
                        val insertEvent = FileChangeEvent(System.currentTimeMillis(),
                            ActionType.INSERT,
                            offset,
                            file.path,
                            oldText,
                            newText)
                        // "[$timestamp],INSERT,$offset,${file.path},\"${StringEscapeUtils.escapeJson(newText)}\",${gson.toJson(newText)}"
                        //gson.toJson(insertEvent)
                        convertToJson(insertEvent)
                    }
                    newText.isEmpty() && oldText.isNotEmpty() -> {
                        val deleteEvent = FileChangeEvent(
                            System.currentTimeMillis(),
                            ActionType.DELETE,
                            offset,
                            file.path,
                            oldText,
                            newText
                        )
                        // "[$timestamp],DELETE,$offset,${file.path},\"${StringEscapeUtils.escapeJson(oldText)}\",${gson.toJson(oldText)}"
                        //gson.toJson(deleteEvent)
                        convertToJson(deleteEvent)
                    }
                    else -> {
                        val modifyEvent = FileChangeEvent(System.currentTimeMillis(),
                            ActionType.UPDATE,
                            offset,
                            file.path,
                            oldText,
                            newText)
//                        "[$timestamp],MODIFY,$offset,${file.path},\"${StringEscapeUtils.escapeJson(oldText)}\",\"${
//                            StringEscapeUtils.escapeJson(
//                                newText
//                            )
//                        }\",${gson.toJson(newText)}, ${gson.toJson(oldText)}"
//                        gson.toJson(modifyEvent)
                        convertToJson(modifyEvent)
                    }
                }

                CFileTrackerUI.addEvent(msg)
                appendToLogFile(Path.of(logFile.toString()), msg)
            }
        }

//        // Create your listener
//        val myEditorListener = object : EditorFactoryListener {
//            override fun editorCreated(event: EditorFactoryEvent) {
//                val editor = event.editor
//                val document = editor.document
//                val file = FileDocumentManager.getInstance().getFile(document)
//
//                if (file == null || file.extension != "c") return
//
//                val project = ProjectManager.getInstance().openProjects.firstOrNull() ?: return
//                // val repo = findGitRepositoryForFile(project, file) ?: return
//
//                document.addDocumentListener(myDocListener)
//            }
//            override fun editorReleased(event: EditorFactoryEvent) {
//
//                val editor = event.editor
//                println("Editor released: ${editor.document.textLength}")
//            }
//        }

        // Add the listener with a project-level disposable
        // EditorFactory.getInstance().addEditorFactoryListener(myEditorListener, project)

        val myDisposer = Disposable {
            EditorFactory.getInstance().eventMulticaster.removeDocumentListener(myDocListener)
        }

        EditorFactory.getInstance().eventMulticaster.addDocumentListener(myDocListener, myDisposer)
        hasDocListenerBeenAdded = true
    }

//    private fun appendToLogFile(path: Path, message: String) {
//        try {
//            Files.writeString(
//                path,
//                message + System.lineSeparator(),
//                StandardOpenOption.CREATE,
//                StandardOpenOption.APPEND
//            )
//        } catch (e: Exception) {
//            log.warn("Failed to write log ${path}: ${e.message}")
//        }
//    }

        // project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, FileContentChangeListener ())
//        EditorFactory.getInstance().eventMulticaster.addDocumentListener(object : DocumentListener {
//            override fun beforeDocumentChange(event: DocumentEvent) {
//                thisLogger().warn("Now to modify doc: ${event.document}")
//                thisLogger().warn("Before: ${event.offset}: '${StringEscapeUtils.escapeJson(event.oldFragment.toString())}'")
//            }
//            override fun documentChanged(event: DocumentEvent) {
//                thisLogger().warn("Done modifying: ${event.document}")
//                thisLogger().warn("After: ${event.offset}: '${StringEscapeUtils.escapeJson(event.newFragment.toString())}'")
//            }
//        })
//    }


}